//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Attached raster map.
//

# include "StdAfx.h"
# include "Map.h"
# include "Load.h"
# include "Position.h"
# include "Scale.h"
# include "DDBitmap.h"
# include "fast_floor.h"
# include "Globals.h"

void renderer_base_t::swap (renderer_base_t & _other) {
	this->file_ref_t::swap (_other);

	this->rect.swap (_other.rect);
	std::swap (this->ptCorrectionShift, _other.ptCorrectionShift);
	std::swap (this->bOverlay,          _other.bOverlay);
}

//////////////////////////////////////////////////

map_renderer_t::map_renderer_t (CMapRenderer * _pMapRenderer) :
	pMapRenderer (_pMapRenderer)
{
	if (_pMapRenderer)
		_pMapRenderer->GetRect (rect, lWidth, lHeight);
}

map_renderer_t::~map_renderer_t () {
	delete pMapRenderer;
}

void map_renderer_t::swap (map_renderer_t & _other) {
	this->renderer_base_t::swap (_other);

	std::swap (this->pMapRenderer, _other.pMapRenderer);
}

void * map_renderer_t::DetachData () {
	map_renderer_t * const pCopy = new map_renderer_t;
	this->swap (* pCopy);
	return pCopy;
}

void map_renderer_t::AttachData (void * _pData) {
	map_renderer_t * const pCopy = reinterpret_cast<map_renderer_t *> (_pData);
	this->swap (* pCopy);
	delete pCopy;
}

void map_renderer_t::FreeData (void * _pData) {
	map_renderer_t * const pCopy = reinterpret_cast<map_renderer_t *> (_pData);
	delete pCopy;
}

void map_renderer_t::Draw (HDC _hDC, const rect_t & _rectVisible, int _nShiftX, int _nShiftY) const {
	if (pMapRenderer)
		pMapRenderer->Draw (_hDC, _rectVisible, ptCorrectionShift, _nShiftX, _nShiftY);
}

//////////////////////////////////////////////////////

image_t::image_t (CBitmap * _pBitmap) : pBitmap (_pBitmap) {}

void image_t::MakeSmall () {
	assert (! this->pBitmapSmall);

	this->pBitmapSmall = this->pBitmap->MakeSmall ();
}

/*void image_t::clear () {
	file_ref_t::clear ();

	this->pBitmap.Release ();
	this->pBitmapSmall.Release ();

	this->rect = rect_t ();
}*/

void image_t::swap (image_t & _other) {
	this->renderer_base_t::swap (_other);

	std::swap (this->pBitmap,      _other.pBitmap);
	std::swap (this->pBitmapSmall, _other.pBitmapSmall);
}

void * image_t::DetachData () {
	image_t * const pCopy = new image_t;
	this->swap (* pCopy);
	return pCopy;
}

void image_t::AttachData (void * _pData) {
	image_t * const pCopy = reinterpret_cast<image_t *> (_pData);
	this->swap (* pCopy);
	delete pCopy;
}

void image_t::FreeData (void * _pData) {
	image_t * const pCopy = reinterpret_cast<image_t *> (_pData);
	delete pCopy;
}

void image_t::Draw (HDC _hDC, const rect_t & _rectVisible, int _nShiftX, int _nShiftY) const {
	rect_t rect;
	rect_t::Intersection (rect, this->rect, _rectVisible);
	if (rect.Empty ())
		return;

	long lImageX0_dst  = XToScreen (rect.x0) + _nShiftX;
	long lImageX1_dst  = XToScreen (rect.x1) + _nShiftX;
	long lImageY0_dst  = YToScreen (rect.y0) + _nShiftY;
	long lImageY1_dst  = YToScreen (rect.y1) + _nShiftY;

	long lImageX0_src  = fast_floor ((rect.x0 - this->rect.x0)*this->pBitmap->Width  ()/this->rect.Width  () + .5f);
	long lImageX1_src  = fast_floor ((rect.x1 - this->rect.x0)*this->pBitmap->Width  ()/this->rect.Width  () + .5f);
	long lImageY0_src  = fast_floor ((this->rect.y1 - rect.y0)*this->pBitmap->Height ()/this->rect.Height () + .5f);
	long lImageY1_src  = fast_floor ((this->rect.y1 - rect.y1)*this->pBitmap->Height ()/this->rect.Height () + .5f);

	long lImageX0_src2 = fast_floor ((rect.x0 - this->rect.x0)*this->pBitmapSmall->Width  ()/this->rect.Width  () + .5f);
	long lImageX1_src2 = fast_floor ((rect.x1 - this->rect.x0)*this->pBitmapSmall->Width  ()/this->rect.Width  () + .5f);
	long lImageY0_src2 = fast_floor ((this->rect.y1 - rect.y0)*this->pBitmapSmall->Height ()/this->rect.Height () + .5f);
	long lImageY1_src2 = fast_floor ((this->rect.y1 - rect.y1)*this->pBitmapSmall->Height ()/this->rect.Height () + .5f);

	const long lImageW = fast_floor (this->rect.Width  ()*g_kX + .5f);
	const long lImageH = fast_floor (this->rect.Height ()*g_kY + .5f);

	if (lImageW >= 5 && lImageH >= 5) {
		bool bDrawBlank = false;
		if (lImageW >= this->pBitmap->Width ()*14 || lImageH >= this->pBitmap->Height ()*14) {
			// TODO: ???
			bDrawBlank = true;
		} else if (lImageW >= this->pBitmap->Width () && lImageH >= this->pBitmap->Height ()) {
			// Use fast approximation for magnification case.
			// NOTE: HALFTONE is rather slow in this case.
			::SetStretchBltMode (_hDC, COLORONCOLOR);
			bDrawBlank = ! this->pBitmap->StretchBlt (
				_hDC,
				lImageX0_dst, lImageY1_dst, lImageX1_dst - lImageX0_dst, lImageY0_dst - lImageY1_dst,
				lImageX0_src, lImageY1_src, lImageX1_src - lImageX0_src, lImageY0_src - lImageY1_src
			);
		} else if (this->pBitmapSmall && lImageW > this->pBitmapSmall->Width () && lImageH > this->pBitmapSmall->Height ()) {
			// Use high quality approximation for the stretching case.
			::SetStretchBltMode (_hDC, HALFTONE);
			bDrawBlank = ! this->pBitmap->StretchBlt (
				_hDC,
				lImageX0_dst, lImageY1_dst, lImageX1_dst - lImageX0_dst, lImageY0_dst - lImageY1_dst,
				lImageX0_src, lImageY1_src, lImageX1_src - lImageX0_src, lImageY0_src - lImageY1_src
			);
		} else if (lImageW > 50 && lImageH > 50) {
			if (! this->bOverlay) {
				if (this->pBitmapSmall) {
					::SetStretchBltMode (_hDC, HALFTONE);
					bDrawBlank = ! this->pBitmapSmall->StretchBlt (
						_hDC,
						lImageX0_dst,  lImageY1_dst,  lImageX1_dst  - lImageX0_dst,  lImageY0_dst  - lImageY1_dst,
						lImageX0_src2, lImageY1_src2, lImageX1_src2 - lImageX0_src2, lImageY0_src2 - lImageY1_src2
					);
				} else
					bDrawBlank = true;
			}
		} else
			bDrawBlank = true;

		if (bDrawBlank && ! this->bOverlay) {
			RECT rect;
			rect.left   = lImageX0_dst;
			rect.top    = lImageY1_dst;
			rect.right  = lImageX1_dst;
			rect.bottom = lImageY0_dst;
			::FillRect (_hDC, & rect, g_hbrLtGray);
		}
	}
}

//////////////////////////////////////////////////////

void JoinRenderers (renderers_t & _renderers1, renderers_t & _renderers2) {
	if (_renderers1.empty ())
		_renderers1.swap (_renderers2);
	else {
		for (renderers_t::iterator ri = _renderers2.begin (); ri != _renderers2.end (); ++ ri) {
			renderer_base_t * const pRenderer = * ri;
			if (pRenderer->bOverlay || pRenderer->bDeleted)
				continue;

			// Move new images to the end of the list.
			const renderers_t::iterator iLast = _renderers1.insert (_renderers1.end (), pRenderer);
			* ri = NULL;

			// Create overlay images.
			if (! pRenderer->IsImage ())
				continue;
			image_t & image1 = * static_cast<image_t *> (pRenderer);

			for (renderers_t::const_iterator ri2 = _renderers1.begin (); ri2 != iLast; ++ ri2) {
				const renderer_base_t * pRenderer2 = * ri2;
				if (pRenderer2->bDeleted || ! pRenderer2->IsImage ())
					continue;
				const image_t & image2 = * static_cast<const image_t *> (pRenderer2);

				if (pRenderer->rect.Intersects (pRenderer2->rect)) {
					image_t * const pOverlayImage = new image_t;
					pOverlayImage->bOverlay = true;

					rect_t::Intersection (pOverlayImage->rect, image1.rect, image2.rect);
					assert (! pOverlayImage->rect.Invalid ());

					// Coordinates of overlapped part inside image1.
					const long lX10 = fast_floor ((pOverlayImage->rect.x0 - image1.rect.x0)/image1.rect.Width  ()*image1.pBitmap->Width  () + .5f);
					const long lX11 = fast_floor ((pOverlayImage->rect.x1 - image1.rect.x0)/image1.rect.Width  ()*image1.pBitmap->Width  () + .5f);
					const long lY10 = fast_floor ((image1.rect.y1 - pOverlayImage->rect.y1)/image1.rect.Height ()*image1.pBitmap->Height () + .5f);
					const long lY11 = fast_floor ((image1.rect.y1 - pOverlayImage->rect.y0)/image1.rect.Height ()*image1.pBitmap->Height () + .5f);

					// Coordinates of overlapped part inside image2.
					const long lX20 = fast_floor ((pOverlayImage->rect.x0 - image2.rect.x0)/image2.rect.Width  ()*image2.pBitmap->Width  () + .5f);
					const long lX21 = fast_floor ((pOverlayImage->rect.x1 - image2.rect.x0)/image2.rect.Width  ()*image2.pBitmap->Width  () + .5f);
					const long lY20 = fast_floor ((image2.rect.y1 - pOverlayImage->rect.y1)/image2.rect.Height ()*image2.pBitmap->Height () + .5f);
					const long lY21 = fast_floor ((image2.rect.y1 - pOverlayImage->rect.y0)/image2.rect.Height ()*image2.pBitmap->Height () + .5f);

					// Create bitmap for the overlay.
					CDDBitmap * const pOverlay = new CDDBitmap (
						__max (lX11 - lX10, lX21 - lX20),
						__max (lY11 - lY10, lY21 - lY20)
					);
					pOverlayImage->pBitmap = pOverlay;

					// Render new image's part.
					::SetStretchBltMode (pOverlay->GetDC (), HALFTONE);
					image1.pBitmap->StretchBlt (pOverlay->GetDC (), 0, 0, pOverlay->Width (), pOverlay->Height (), lX10, lY10, lX11 - lX10, lY11 - lY10);
				
					// Render old image's part using alpha blending.
					if (g_fpAlphaBlend) {
						image2.pBitmap->AlphaBlend (
							pOverlay->GetDC (),
							0, 0, pOverlay->Width (), pOverlay->Height (),
							lX20, lY20, lX21 - lX20, lY21 - lY20,
							96
						);
					}

					pOverlayImage->MakeSmall ();

					_renderers1.push_back (pOverlayImage);
				}
			}
		}

		_renderers2.clear ();
	}

	UpdateOverlays (_renderers1);
}

void UpdateOverlays (renderers_t & _renderers) {
	for (renderers_t::iterator ri = _renderers.begin (); ri != _renderers.end (); ++ ri) {
		renderer_base_t * const pRendererOverlay = * ri;
		// For each overlay image...
		if (! pRendererOverlay->bOverlay || pRendererOverlay->bDeleted)
			continue;

		// Count non-overlay images which cover the overlay one.
		size_t cParents = 0;
		for (renderers_t::const_iterator ri2 = _renderers.begin (); ri2 != _renderers.end (); ++ ri2) {
			const renderer_base_t * const pRendererNotOverlay = * ri2;
			if (
				& pRendererOverlay == & pRendererNotOverlay ||
				pRendererNotOverlay->bOverlay || pRendererNotOverlay->bDeleted || ! pRendererNotOverlay->bVisible
			)
				continue;

			if (pRendererNotOverlay->rect.Contains (pRendererOverlay->rect))
				++ cParents;
		}

		pRendererOverlay->bVisible = cParents >= 2;
	}
}

bool IsRenderer (const map_t & _map, const file_ref_t & _fr) {
	for (renderers_t::const_iterator ri = _map.renderers.begin (); ri != _map.renderers.end (); ++ ri)
		if (* ri == & _fr)
			return true;
	return false;
}